﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Task_When_GetResult.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the Task_When_GetResult type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Threading
{
    using System;
    using System.Threading;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Testing;
    using Testing.Threading;

    [TestClass]
    public class Task_When_GetResult : AsynchronousTest
    {
        [TestMethod]
        [HostType("CHESS")]
        public void If_Completed_And_Task_Returns_Should_Callback_With_Value()
        {
            var result = TestStrings.Failure;
            Task.Factory.From(() => TestStrings.Success)
                .Start(t => result = t.Result);
            AsyncUtility.SleepUntil(() => result == TestStrings.Success);
        }

        [TestMethod]
        [HostType("CHESS")]
        public void If_Completed_And_Task_Throws_Should_Callback_With_Exception()
        {
            var result = TestStrings.Failure;

            Func<string> taskFunc = () => { throw new Exception(TestStrings.Success); };
            Task.Factory.From(taskFunc)
                .Start(
                    t =>
                        {
                            try
                            {
                                result = t.Result;
                            }
                            catch (Exception e)
                            {
                                result = e.Message;
                            }
                        });
            AsyncUtility.SleepUntil(() => result == TestStrings.Success);
        }

        [TestMethod]
        [HostType("CHESS")]
        public void If_Started_And_Not_Complete_And_Task_Returns_Should_Block_Until_Complete()
        {
            var thread = Thread.CurrentThread;
            var task = Task.Factory.From(
                () =>
                    {
                        AsyncUtility.SleepUntilThreadIsInState(thread, ThreadState.WaitSleepJoin);
                        return TestStrings.Success;
                    });
            task.Start();
            // access result, should go into Wait state and release task for completion.
            var result = task.Result;
            Assert.AreEqual(TestStrings.Success, result);
        }

        [TestMethod]
        [HostType("CHESS")]
        public void If_Started_But_Not_Complete_And_Task_Throws_Should_Block_Until_Complete_With_Exception()
        {
            var thread = Thread.CurrentThread;
            Func<int> taskFunc = () =>
                {
                    AsyncUtility.SleepUntilThreadIsInState(thread, ThreadState.WaitSleepJoin);
                    throw new Exception(TestStrings.Success);
                };
            var task = Task.Factory.From(taskFunc);
            task.Start();
            // access result, should go into Wait state and release task for completion.
            AssertException.Throws<Exception>(() => { var result = task.Result; }, TestStrings.Success);
        }

        [TestMethod]
        [HostType("CHESS")]
        public void If_Is_Not_Started_Should_Block_Until_Started_And_Completed()
        {
            var testThread = Thread.CurrentThread;

            var task = Task.Factory.From(() => TestStrings.Success);
            // start on another thread
            ThreadPool.QueueUserWorkItem(s =>
                {
                    AsyncUtility.SleepUntilThreadIsInState(testThread, ThreadState.WaitSleepJoin);
                    task.Start();
                });
            Assert.AreEqual(TestStrings.Success, task.Result);
        }

        [TestMethod]
        [HostType("CHESS")]
        public void If_Start_And_GetResult_Are_Called_From_Different_Threads()
        {
            var task = Task.Factory.From(() => TestStrings.Success);

            var readResult = TestStrings.Failure;
            ThreadPool.QueueUserWorkItem(s => { readResult = task.Result; });
            task.Start();

            AsyncUtility.SleepUntil(() => readResult == TestStrings.Success);
        }

        /// <summary>
        ///   Tests the <see cref="Task{T}" /> blocks until its result is available.
        /// </summary>
        /// <remarks>
        ///   Somehow, other tests did not pick up an issue here. It was possible for Result to return before its value had been assigned.
        /// </remarks>
        [TestMethod]
        [HostType("CHESS")]
        public void If_Started_And_Not_Complete_And_Task_Is_Long_Running_And_Returns_Should_Block_Until_Complete()
        {
            var task = Task.Factory.From(
                () =>
                    {
                        Thread.Sleep(100);
                        return TestStrings.Success;
                    });
            task.Start();
            // access result, should go into Wait state and release task for completion.
            Assert.AreEqual(TestStrings.Success, task.Result);
        }
    }
}
